import type {
  Expression, Identifier, Literal, Token,
} from '@/types/retrieval-sql'
import { syntaxError, typeError } from '@/utils/retrieval-sql/utils'
import type Target from './Target'
import { joinExpression } from '@/utils/retrieval-sql/core/parser/index'

/**
 * 对单个表达式对行解析，一个语句中有多个子语句： "(t1 = xx )" and "(ti = xx)"
 */
class ExpressionTarget {
  // 一条完整语句
  target

  // 当前单个表达式解析值
  expression: Expression | undefined

  // 标识符
  identifier

  // 连接关系符
  operation

  // 是否有包裹括号 ( TI = "("xx ")" )
  parcelBracket = false

  // 括号数组，（ +1 ， ）-1
  bracket: Token[] = []

  constructor(target: Target, identifier: Token, operation: '=' | '<' | '>' | '>=' | '<=') {
    this.target = target
    this.identifier = <Identifier>{
      type: 'Identifier',
      variant: 'column',
      name: identifier.value,
      start: identifier.start,
      end: identifier.end,
    }
    this.operation = operation
  }

  get token() {
    return this.target.token
  }

  hasNext() {
    return this.target.hasNext()
  }

  next(filterWhitespace = true) {
    return this.target.next(filterWhitespace)
  }

  getToken(next = 0, filterWhitespace = true) {
    return this.target.getToken(next, filterWhitespace)
  }

  lastToken() {
    return this.target.lastToken()
  }

  startBracket() {
    this.bracket.push(this.token!)
  }

  endBracket() {
    this.bracket.pop()
  }

  walk() {
    while (this.hasNext()) {
      // eslint-disable-next-line no-use-before-define
      const bool = walks.every((handler) => handler.call(this))
      if (!bool) {
        break
      }
    }

    // TI = ()
    if (!this.expression) {
      throw syntaxError({
        message: '缺少查询内容',
        start: this.identifier.start,
        end: this.token?.end ?? this.lastToken().end,
      })
    }

    // 处理结束括号 TI = (标题")"
    if (this.bracket.length) {
      throw syntaxError({
        message: '括号未结束',
        start: this.bracket[this.bracket.length - 1].start,
        end: this.bracket[this.bracket.length - 1].end,
      })
    }
    return this.expression
  }
}

/**
 * 解析括号，不是括号为错误语句
 */
function walkPunctuator(this: ExpressionTarget): boolean {
  while (this.token?.type === 'Punctuator' || this.token?.type === 'Whitespace') {
    if (this.token.type === 'Whitespace') {
      this.next()
      continue
    }
    const { value, start, end } = this.token
    // 开始括号 1. TI = "("标题 专题) || 2. TI = 1 OR (标题 NOT 专题)
    if (value === '(') {
      const previous = this.getToken(-1)
      if (previous?.type === 'Identifier' || (previous?.type === 'Punctuator' && previous.value === ')')) {
        throw syntaxError({
          message: '缺少逻辑运算符',
          start: previous.start,
          end,
        })
      }

      this.startBracket()
      if (!this.expression) {
        this.parcelBracket = true
      }
      this.next()
      continue
    }
    // 结束括号 TI = (标题 专题")"
    if (value === ')') {
      // 语句错误, 内容为空
      if (this.bracket[this.bracket.length - 1]?.value !== '(') {
        return false
      }
      this.endBracket()
      this.next()
      continue
    }

    throw syntaxError({
      message: '符号使用错误',
      start,
      end,
    })
  }
  return true
}

function walkJoin(this: ExpressionTarget): boolean {
  if (this.token?.type === 'Whitespace') {
    this.next()
  }
  if (this.token?.type !== 'Keyword') {
    return true
  }
  const { start, end, value } = this.token

  if (!this.expression || this.getToken(-1)?.type === 'Keyword') {
    throw syntaxError({
      message: `如需查询运算符, 请使用引号包裹 "${value}"`,
      start,
      end,
    })
  }

  // 1. ti = 1 and "or" 2.  ti = 1 "+" and
  if (this.getToken(-1)?.type === 'Punctuator' && this.getToken(-1)?.value !== ')') {
    throw syntaxError({
      message: '无效的检索式',
      start: this.token!.start,
      end: this.token!.end,
    })
  }

  if (!this.getToken(1)) {
    throw syntaxError({
      message: '缺少后续查询',
      start: this.lastToken().start,
      end: this.lastToken().end,
    })
  }

  // 检查是否下个条件进行退出 ExpressionTarget 标题=(系统 OR LED) NOT "(标题=方法)"
  let current = 1
  let hasIdentifier = false
  while (this.getToken(current)) {
    const node = this.getToken(current)!
    if (node.type === 'Punctuator') {
      if (node.value === '(') {
        current += 1
        continue
      }

      if (['=', '<', '>'].includes(node.value) && hasIdentifier) {
        return false
      }
      break
    } if (node.type === 'Identifier') {
      if (!hasIdentifier) {
        hasIdentifier = true
        current += 1

        // (精确) 判定
        if (this.getToken(current + 1, false)?.value === '(' && this.getToken(current + 2, false)?.value === '精确' && this.getToken(current + 3, false)?.value === ')') {
          current += 3
        }
        continue
      }
      break
    } if (node.type === 'Keyword') {
      break
    } else if (node.type === 'Whitespace') {
      continue
    }
    throw typeError({
      message: `内部错误，Token 未知类型 ${node.type}`,
      start: node.start,
      end: node.end,
    })
  }

  this.expression = joinExpression(this.expression, this.token, this.getToken(-1))
  this.next()

  // 遇到括号进行优先连接
  const token = this.token
  if (token?.type === 'Punctuator' && token.value === '(') {
    const expression = new ExpressionTarget(
      this.target,
      {
        type: 'Identifier',
        value: this.identifier.name,
        start: this.identifier.start,
        end: this.identifier.end,
      },
      this.operation,
    )
      .walk()
    if (!expression) {
      throw syntaxError({
        message: '缺少查询内容',
        start: token.start,
        end: this.lastToken().end,
      })
    }
    if (!this.expression.right) {
      this.expression.right = expression
    } else {
      (this.expression.right as Expression).right = expression
    }
  }

  return true
}

function walkLiteral(this: ExpressionTarget): boolean {
  if (!this.token) {
    if (!this.expression) {
      throw syntaxError({
        message: '查询内容为空',
        start: this.lastToken().start,
        end: this.lastToken().end,
      })
    }
    return false
  }
  if (this.token?.type === 'Whitespace') {
    this.next()
  }

  const { type, start } = this.token
  let { end, value } = this.token
  if (type === 'Identifier') {
    // 对查询值判断，进行追加. 例：s7=xx(带括号)公司 、专利数据库=纳米>纳米生物
    let bracket = 0
    while (this.hasNext()) {
      // 提前判断退出，所以不能使用 next()
      const previous = this.token
      const token = this.getToken(1, false)
      const next = this.getToken(2, false)

      // 空格退出匹配
      if (!token || token.type === 'Whitespace') {
        break
      }
      // ) 判定退出
      if (token.type === 'Punctuator' && token.value === ')') {
        if (previous?.type === 'Punctuator' && previous.value === '(') {
          throw syntaxError({
            message: '括号内容为空',
            start: previous.start,
            end: token.end,
          })
        }

        if (bracket === 0) {
          // 退出条件：结束 、空格 、 双结束括号
          if (!next || next.type === 'Whitespace' || (next.type === 'Punctuator' && next.value === ')')) {
            break
          }
        } else {
          bracket -= 1
        }
      } else if (token.type === 'Punctuator' && token.value === '(') {
        if (previous?.type === 'Punctuator' && previous.value === ')') {
          throw syntaxError({
            message: '缺少逻辑运算符',
            start: previous.start,
            end: token.end,
          })
        }
        bracket += 1
      }

      end = token.end
      value += token.value
      this.next(false)
    }

    // TI = "标题"
    const expression: Expression<Identifier, Literal> = {
      type: 'Expression',
      variant: 'operation',
      operation: this.operation,
      start: this.identifier.start,
      end,
      left: this.identifier,
      right: {
        type: 'Literal',
        variant: 'text',
        value,
        start,
        end,
      },
    }
    const operation = (this.getToken(-1)?.type === 'Keyword' ? this.getToken(-1)!.value : 'AND') as any

    if (!this.expression) {
      this.expression = expression
    } else if (!this.expression.right) {
      // TI = ( v1 AND v2 ) v1 和 v2 之前关键处理了一层

      this.expression.right = expression
      this.expression.end = end
    } else if (this.expression.right.type === 'Expression' && !this.expression.right.right) {
      // 优先级的结构转换
      this.expression.right.right = expression
      this.expression.right.end = end
    } else if (this.expression.right?.type === 'Literal') {
      // TI = ( v1 v2 ) v1 和 v2 之前省略了关键字
      this.expression = {
        type: 'Expression',
        operation,
        variant: 'operation',
        start: this.expression.start,
        end,
        left: this.expression,
        right: expression,
      }
    } else if (this.expression.right.type === 'Expression' && this.expression.right.right?.type === 'Literal') {
      // TI = ( v1 v2 ) v1 和 v2 之前省略了关键字

      this.expression.right = {
        ...this.expression,
        right: {
          type: 'Expression',
          operation,
          variant: 'operation',
          start: this.expression.right.start,
          end,
          left: this.expression.right,
          right: expression,
        },
        end,
      }
    } else {
      throw typeError({
        message: '内部错误，未知的结构类型',
        start: this.expression.start,
        end,
      })
    }

    this.next()
    return true
  }

  if (type === 'Keyword') {
    throw syntaxError({
      message: `如需查询运算符, 请使用引号包裹 "${value}"`,
      start,
      end,
    })
  }
  return true
}

const walks: ((this: ExpressionTarget) => boolean)[] = [
  walkPunctuator, // ((
  walkJoin, // and not or
  walkLiteral, // xx
  walkPunctuator, // ))
  walkJoin, // and not or
]

export default ExpressionTarget
